package br.usp.pcs.basys2.job;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import br.usp.pcs.basys2.BasysProcess;
import br.usp.pcs.basys2.info.JobInfo;
import br.usp.pcs.basys2.info.ProcessInfo;

public class JobManager {

	private List<JobInfo> activeJobs = new ArrayList<JobInfo>();
	private List<JobInfo> waitingJobs = new ArrayList<JobInfo>();
	// private Map<Long, JobInfo> map = new HashMap<Long, JobInfo>();
	private Map<JobInfo, List<ProcessInfo>> processMap;
	private int maxActiveJobs = 5;

	private List<JobManagerListener> listeners = new ArrayList<JobManagerListener>();

	public boolean add(JobManagerListener e) {
		return listeners.add(e);
	}

	public void manage(JobInfo job) {
		if (activeJobs.size() >= maxActiveJobs) {
			notifyListenersQueueOf(job);
			waitingJobs.add(job);
		} else {
			active(job);
		}
	}

	public void setMaxActiveJobs(int maxActiveJobs) {
		this.maxActiveJobs = maxActiveJobs;
	}

	private void active(JobInfo job) {
		if (processMap == null) {
			processMap = new TreeMap<JobInfo, List<ProcessInfo>>();
		}
		activeJobs.add(job);
		List<ProcessInfo> allProcessInfos = getAllProcessInfos(job);
		processMap.put(job, allProcessInfos);
		notifyListenersActivationOf(job);
		// map.put(job.getTopProcess().getId(), job);
	}

	private List<ProcessInfo> getAllProcessInfos(JobInfo job) {
		ProcessInfo info = job.getTopProcess();
		List<ProcessInfo> list = getAllChildProcessInfos(info);
		return list;
	}

	private List<ProcessInfo> getAllChildProcessInfos(ProcessInfo info) {
		ArrayList<ProcessInfo> list = new ArrayList<ProcessInfo>();
		list.add(info);
		List<ProcessInfo> childProcesses = info.getChildProcesses();
		for (ProcessInfo processInfo : childProcesses) {
			list.addAll(getAllChildProcessInfos(processInfo));
		}
		return list;
	}

	public void freeProcess(BasysProcess process) {
		ProcessInfo info = process.getInfo();
		for (Iterator<JobInfo> iterator = processMap.keySet().iterator(); iterator
				.hasNext();) {
			JobInfo jobInfo = iterator.next();
			List<ProcessInfo> processList = processMap.get(jobInfo);
			for (Iterator<ProcessInfo> iterator2 = processList.iterator(); iterator2
					.hasNext();) {
				ProcessInfo processInfo = (ProcessInfo) iterator2.next();
				if (processInfo.equals(info)) {
					iterator2.remove();
					if (processList.isEmpty()){
						free(jobInfo);
						return;
					}
				}
			}
		}
		/*
		 * for (JobInfo jobInfo : keySet) { List<ProcessInfo> list =
		 * processMap.get(jobInfo); ProcessInfo info = process.getInfo(); if
		 * (list.remove(info) && list.isEmpty()) { //
		 * processMap.remove(jobInfo); free(jobInfo); } }
		 */
	}

	private void free(JobInfo job) {
		activeJobs.remove(job);
		if (!waitingJobs.isEmpty()) {
			JobInfo nextJob = waitingJobs.remove(0);
			active(nextJob);
		}
		notifyListenersDeactivationOf(job);
	}

	private void notifyListenersQueueOf(JobInfo job) {
		for (JobManagerListener listener : listeners) {
			listener.onJobIsQueued(job);
		}
	}

	private void notifyListenersActivationOf(JobInfo job) {
		for (JobManagerListener listener : listeners) {
			listener.onJobIsActivated(job);
		}
	}

	private void notifyListenersDeactivationOf(JobInfo job) {
		for (JobManagerListener listener : listeners) {
			listener.onJobIsDeactivated(job);
		}
	}

}
